package trabalho_codificacao_real;

import java.util.ArrayList;
import java.util.List;
import util.MersenneTwister;

/**
 *
 * @author Guilherme
 */
public class CodificacaoReal {

    public class Individuo {

        private double[] genes;
        private double fitness;
        private boolean mod;

        public Individuo(double[] genes) {
            this.genes = genes;
            this.calcularFitness();
        }

        public double[] getGenes() {
            return genes;
        }

        public double getFitness() {
            if (mod) {
                this.calcularFitness();
            }
            return fitness;
        }

        private double calcularFitness() {
            double soma = 0;
            for (int i = 0; i < genes.length; i++) {
                soma += this.genes[i] > 1.0 ? 1.0 : this.genes[i];
            }
            mod = false;
            return (fitness = soma / genes.length);
        }

        public void setGene(int x, double valor) {
            this.genes[x] = valor;
            mod = true;
        }

        public double getGene(int x) {
            return this.genes[x] > 1.0 ? 1.0 : this.genes[x];
        }

    };

    private MersenneTwister random = new MersenneTwister(System.nanoTime());

    private List<Individuo> populacao;
    private int tamanho_ind = 5;
    private double desvio_mutacao = 0.1;

    public CodificacaoReal() {
        populacao = new ArrayList();
    }    
    
    public CodificacaoReal(int tamanho_individuo) {
        this();
        tamanho_ind = tamanho_individuo;
    }

    public Individuo gerarNovoIndividuo() {
        double[] cod = new double[tamanho_ind];
        for (int i = 0; i < cod.length; i++) {
            cod[i] = random.nextDouble();
        }
        Individuo ind = new Individuo(cod);
        this.addOrdenadoPopulacao(ind);
        return ind;
    }

    public void mutarIndividuo(Individuo ind) {
        mutarIndividuo(ind, desvio_mutacao);
    }
    
    public void mutarIndividuo(Individuo ind, Double var) {        
        this.removeIndividuo(ind);
        double min = Double.MAX_VALUE; int idx_min = -1;
        for (int i = 0; i < ind.getGenes().length; i++) {
            if(min > ind.getGene(i)){
                min = ind.getGene(i);
                idx_min = i;
            }
        }
        double desvio = var;
        double valor = ind.getGene(idx_min) + desvio * random.nextGaussian();
        ind.setGene(idx_min, valor);
        this.addOrdenadoPopulacao(ind);
    }    

    private void addOrdenadoPopulacao(Individuo ind) {
        if (populacao.isEmpty() || populacao.get(populacao.size() - 1).getFitness() >= ind.getFitness()) {
            populacao.add(ind);
        } else {
            for (int y = 0; y < populacao.size(); y++) {
                if (populacao.get(y).getFitness() < ind.getFitness()) {
                    populacao.add(y, ind);
                    break;
                }
            }
        }
    }

    public void printPopulação(int interacao) {
        System.out.println("---------------------------------- Interação: " + interacao);
        for (int j = 0; j < populacao.size(); j++) {
            System.out.println(j + " - " + populacao.get(j).getFitness());
        }
        System.out.println("");
    }

    public Individuo getIndividuo(int x) {
        return populacao.get(x);
    }

    public Individuo removeIndividuo(int x){
        return populacao.remove(x);
    }
    
    public boolean removeIndividuo(Individuo ind){
        return populacao.remove(ind);
    }    
    
}
